package com.vhall.component.service.redpacket.service.impl;

/**
 * @author xuyongzheng
 * @since 2021/3/20 22:42
 **/

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.vhall.component.dao.redpacket.RedPacketMapper;
import com.vhall.component.entity.rebroadcast.RedPacketEntity;
import com.vhall.component.entity.rebroadcast.RedPacketRecordEntity;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.plugin.common.annotation.RedisSpinLock;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.service.redpacket.common.RedpacketConstants;
import com.vhall.component.service.redpacket.service.CacheService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.ZoneOffset;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import static jodd.util.ThreadUtil.sleep;

@Service
public class CacheServiceImpl implements CacheService {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Value("${queue.run.rate:0}")
    private Integer queueRun;

    @Autowired
    private RedPacketMapper redPacketMapper;

    @Override
    public Boolean cacheStat(String appId, String sourceId, Long redPacketId, String thirdPartyUserId, BigDecimal amount) {

        String upkey = RedpacketConstants.COUNT_RED_PACKET_USER_BY_RED_PACKET_ID + redPacketId;
        redisTemplate.opsForSet().add(upkey, thirdPartyUserId);

        // 按照 source_id 统计的用户参与表
        String uskey = RedpacketConstants.COUNT_RED_PACKET_USER_BY_SOURCE_ID + appId + "_" + sourceId;
        redisTemplate.opsForSet().add(uskey, thirdPartyUserId);


        //将参加过红包活动的用户添加到redis中
        String akey = RedpacketConstants.RED_PACKET_USER_BY_APP_ID + appId + "_" + redPacketId;
        redisTemplate.opsForSet().add(akey, thirdPartyUserId);
        redisTemplate.expire(akey, 2, TimeUnit.DAYS);

        Double money = amount.doubleValue();

        // APP ID 维度下红包总金额
        String amountK = RedpacketConstants.COUNT_RED_PACKET_AMOUNT_BY_RED_PACKET_ID + appId;
        redisTemplate.opsForHash().increment(amountK, redPacketId.toString(), money);

        // Source ID维度下红包总金额
        String amtSource = RedpacketConstants.COUNT_RED_PACKET_AMOUNT_BY_SOURCE_ID + appId;
        redisTemplate.opsForHash().increment(amtSource, sourceId, money);

        return true;
    }


    @Override
    public void pushToQueue(RedPacketRecordEntity dto) {
        String qName = RedpacketConstants.QUEUE_RED_PACKET_RECORD;
        redisTemplate.opsForSet().add(RedpacketConstants.QUEUE_NAMES_KEY, qName);
        redisTemplate.opsForList().rightPush(qName, JSON.toJSONString(dto));
    }

    @Override
    public void cacheRecords(RedPacketRecordEntity dto) {

        // 排序, 按照金额排行明细
        String amtRank = String.format(RedpacketConstants.RED_RECORDS_ORDER_BY_AMOUNT_RANKING, dto.getRedPacketId());
        redisTemplate.opsForZSet().add(amtRank, JSON.toJSONString(dto), dto.getAmountRanking().doubleValue());
        redisTemplate.expire(amtRank, 2, TimeUnit.DAYS);

        // 排序, 按照时间
        String timeRank = String.format(RedpacketConstants.RED_RECORDS_ORDER_BY_TIME, dto.getRedPacketId());
        redisTemplate.opsForZSet().add(timeRank, JSON.toJSONString(dto), dto.getCreatedAt().toInstant(ZoneOffset.of("+8")).toEpochMilli());
        redisTemplate.expire(timeRank, 2, TimeUnit.DAYS);

    }

    @Override
    public String popFromQueue(String key, Boolean block, Integer timeOut) {
        block = block == null || block;
        timeOut = (timeOut == null) ? 5 : timeOut;
        waitRun();
        String value;
        if (block) {
            value = redisTemplate.opsForList().leftPop(key, timeOut, TimeUnit.SECONDS);
            if (StringUtils.isNotEmpty(value)) {
                return value;
            }
        } else {
            value = redisTemplate.opsForList().leftPop(key);
        }
        return value;
    }


    @Override
    public void clearRedPacketCache(RedPacketEntity redPacket) {
        String qk = RedpacketConstants.QUEUE_RED_PACKET_PREFIX + redPacket.getId();

        redisTemplate.delete(qk);
        redisTemplate.opsForSet().remove(RedpacketConstants.QUEUE_NAMES_KEY, qk);

        redisTemplate.delete(RedpacketConstants.COUNT_RED_PACKET_JOIN_BY_RED_PACKET_ID + redPacket.getId());
        redisTemplate.delete(RedpacketConstants.COUNT_RED_PACKET_USER_BY_RED_PACKET_ID + redPacket.getId());

        /*redisTemplate.opsForHash().delete(Constants.COUNT_RED_PACKET_AMOUNT_BY_RED_PACKET_ID + redPacket.getAppId(),
                                          redPacket.getId().toString());*/
    }

    /**
     * 从缓存中获取红包被领取数据列表
     *
     * @param redPacketId
     * @param offset
     * @param size
     * @param order
     * @param orderType   默认正序
     * @return
     */
    @Override
    public Set<String> selectRedPacketRecords(Long redPacketId, Integer offset, Integer size, String order, String orderType) {
        Set<String> set = null;
        if ("amount_ranking".equals(order)) {
            String amtKey = RedpacketConstants.RED_RECORDS_ORDER_BY_AMOUNT_RANKING.replace("%s", redPacketId.toString());
            if ("desc".equalsIgnoreCase(orderType)) {
                set = redisTemplate.opsForZSet().reverseRange(amtKey, offset, offset + size - 1L);
            } else {
                set = redisTemplate.opsForZSet().range(amtKey, offset, offset + size - 1L);
            }
        } else {
            String timeKey = RedpacketConstants.RED_RECORDS_ORDER_BY_TIME.replace("%s", redPacketId.toString());
            if ("desc".equalsIgnoreCase(orderType)) {
                set = redisTemplate.opsForZSet().reverseRange(timeKey, offset, offset + size - 1L);
            } else {
                set = redisTemplate.opsForZSet().range(timeKey, offset, offset + size - 1L);
            }
        }
        return set;
    }

    @Override
    @RedisSpinLock(redisKey = "#uuid", expireTime = 10 * 1000)
    public RedPacketEntity cacheAndHadleRedPacket(String uuid, String appId) {
        String cache = redisTemplate.opsForValue().get(RedpacketConstants.REDPACKET_INFO + uuid);
        if (StringUtils.isNotBlank(cache)) {
            return JSON.parseObject(cache, RedPacketEntity.class);
        } else {
            LambdaQueryWrapper<RedPacketEntity> query = new LambdaQueryWrapper<>();
            query.eq(RedPacketEntity::getUuid, uuid);
            query.eq(RedPacketEntity::getAppId, appId);
            query.last("limit 1");
            RedPacketEntity redPacketEntity = redPacketMapper.selectOne(query);
            if (null == redPacketEntity) {
                throw new BusinessException(BizErrorCode.COMP_REDPACKET_DELETE_ERROR);
            }
            String key = RedpacketConstants.REDPACKET_INFO + uuid;
            redisTemplate.opsForValue().set(key, JSON.toJSONString(redPacketEntity), 24, TimeUnit.HOURS);
            return redPacketEntity;
        }
    }

    /**
     * 取回队列数据前先执行等待,默认不等待
     *
     * @param
     */
    private void waitRun() {
        Integer runRate = queueRun == null ? 0 : queueRun;
        if (runRate > 0) {
            int maxWaitTime = 1000000;
            if (runRate < maxWaitTime) {
                int waitTime = maxWaitTime / runRate;
                sleep(waitTime);
            }
        }
    }
}
